#ifndef MOCHA_DETAIL_EXPORTED_IMAGE_PLUGIN_HPP_INCLUDED
#define MOCHA_DETAIL_EXPORTED_IMAGE_PLUGIN_HPP_INCLUDED

/**
 * @file
 */

#include <cstdint>
#include <boost/filesystem.hpp>
#include "mocha/color.hpp"
#include "mocha/exported_object.hpp"
#include "mocha/image_plugin.hpp"

namespace mocha { namespace graphics {
/**
 * 
 * @param P 
 * @param I Interface through which the plugin communicates with the core.
 * @see c_image_plugin
 * @see image_plugin
 */
template<typename P, typename I = c_image_plugin>
class exported_image_plugin : public exported_object<P, I, c_image_plugin, image_plugin>, private c_image_plugin, public image_plugin {
public:
	exported_image_plugin() {
		c_image_plugin::self = static_cast<c_image_plugin*>(this);
		c_image_plugin::type_name = &exported_image_plugin<P, I>::type_name;
		c_image_plugin::load_from_file = &exported_image_plugin<P, I>::load_from_file;
		c_image_plugin::load_from_file_buffer = &exported_image_plugin<P, I>::load_from_file_buffer;
		c_image_plugin::load_from_pixels = &exported_image_plugin<P, I>::load_from_pixels;
		c_image_plugin::free = &exported_image_plugin<P, I>::free;
		c_image_plugin::crop = &exported_image_plugin<P, I>::crop;
		c_image_plugin::scale = &exported_image_plugin<P, I>::scale;
		c_image_plugin::convert = &exported_image_plugin<P, I>::convert;
		c_image_plugin::read_write_pixels = &exported_image_plugin<P, I>::read_write_pixels;
		c_image_plugin::read_pixels = &exported_image_plugin<P, I>::read_pixels;
		c_image_plugin::format = &exported_image_plugin<P, I>::format;
		c_image_plugin::width = &exported_image_plugin<P, I>::width;
		c_image_plugin::height = &exported_image_plugin<P, I>::height;
		c_image_plugin::loaded = &exported_image_plugin<P, I>::loaded;
	}
	virtual ~exported_image_plugin() {}
private:
	static const plugin_char_t* type_name(const c_image_plugin* object) {
		return MOCHA_DETAIL_CONST_SELF_CALL(P, object, type_name)();
	}
	static std::int32_t load_from_file(c_image_plugin* object, const plugin_char_t* path) {
		return MOCHA_DETAIL_SELF_CALL(P, object, load)(boost::filesystem::path(reinterpret_cast<const char*>(path)));
	}
	static std::int32_t load_from_file_buffer(c_image_plugin* object, std::int8_t* buffer, std::uint32_t size) {
		return MOCHA_DETAIL_SELF_CALL(P, object, load)(buffer, size);
	}
	static std::int32_t load_from_pixels(c_image_plugin* object, std::int8_t* buffer, std::uint32_t width, std::uint32_t height, std::int32_t red, std::int32_t green, std::int32_t blue, std::int32_t alpha, std::int8_t type) {
		pixel_format format = { { red, green, blue, alpha }, static_cast<pixel_data_type>(type) };
		return MOCHA_DETAIL_SELF_CALL(P, object, load)(buffer, width, height, format);
	}
	static void free(c_image_plugin* object) {
		MOCHA_DETAIL_SELF_CALL(P, object, free)();
	}
	static std::int32_t crop(c_image_plugin* object, std::int32_t x, std::int32_t y, std::int32_t width, std::int32_t height) {
		return MOCHA_DETAIL_SELF_CALL(P, object, crop)(region<std::int32_t>(x, y, width, height));
	}
	static std::int32_t scale(c_image_plugin* object, std::uint32_t width, std::uint32_t height) {
		return MOCHA_DETAIL_SELF_CALL(P, object, scale)(width, height);
	}
	static std::int32_t convert(c_image_plugin* object, std::int32_t red, std::int32_t green, std::int32_t blue, std::int32_t alpha, std::int8_t type) {
		pixel_format format = { { red, green, blue, alpha }, static_cast<pixel_data_type>(type) };
		return MOCHA_DETAIL_SELF_CALL(P, object, convert)(format);
	}
	static void read_write_pixels(c_image_plugin* object, std::int8_t** buffer, std::uint32_t* size) {
		MOCHA_DETAIL_SELF_CALL(P, object, pixels)(*buffer, size);
	}
	static void read_pixels(const c_image_plugin* object, const std::int8_t** buffer, std::uint32_t* size) {
		MOCHA_DETAIL_CONST_SELF_CALL(P, object, pixels)(*buffer, size);
	}
	static std::int32_t format(const c_image_plugin* object, std::int32_t* red, std::int32_t* green, std::int32_t* blue, std::int32_t* alpha, std::int8_t* type) {
		pixel_format format;
		std::int32_t r = MOCHA_DETAIL_CONST_SELF_CALL(P, object, format)(format);
		if (0 > r) {
			return r;
		}
		*red = format.mask.red;
		*green = format.mask.green;
		*blue = format.mask.blue;
		*alpha = format.mask.alpha;
		*type = format.type;
		return r;
	}
	static std::uint32_t width(const c_image_plugin* object) {
		return MOCHA_DETAIL_CONST_SELF_CALL(P, object, width)();
	}
	static std::uint32_t height(const c_image_plugin* object) {
		return MOCHA_DETAIL_CONST_SELF_CALL(P, object, height)();
	}
	static std::int8_t loaded(const c_image_plugin* object) {
		return MOCHA_DETAIL_CONST_SELF_CALL(P, object, loaded)();
	}
};
}}

#endif

